# PDFDecryper-3.5
# Auther:tianwenxiao
# mail:tianwenxiao@wo.cn
# 主要功能:
#        2.0：针对没有设置打开密码的PDF文件，只是设置了打印、编辑等限制的密码，可以使用该脚本进行解密
#        3.5: 新增：针对设置了打开密码的文件，增加了密码字典库爆破功能，只需要选择需要破解的PDF文件，再选择用于破解的字典库，点击执行脚本即可开始破解
import os
import threading
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
#pdf的读取方法
from PyPDF2 import PdfReader
#pdf的写入方法
from PyPDF2 import PdfWriter
#高加密的方法，要引入不然会报错
from Crypto.Cipher import AES
from tkinter import ttk

from pyparsing import Or
# 设置一个终止标识
stop_thread = False

def count_lines_in_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        return sum(1 for _ in file)

def select_input_file(root, input_file_path_var):
    input_file_path = filedialog.askopenfilename(filetypes=[("PDF files", "*.PDF;*.pdf")])
    if input_file_path:
        input_file_path_var.delete(0, tk.END)
        input_file_path_var.insert(0, input_file_path)
    return input_file_path

def select_cracker_dic(root,input_crack_dic_var):
    crack_dic = filedialog.askopenfilename(filetypes=[("TXT file","*.txt;*.TXT")])
    if crack_dic:
        input_crack_dic_var.delete(0,tk.END)
        input_crack_dic_var.insert(0,crack_dic)
        return crack_dic



# 定义一个函数来读取PDF文件
def get_reader(filename, password=None):

    try:
        old_file = open(filename, 'rb')
    except Exception as err:
        return print('文件打开失败！' + str(err))
    #如果使用python2需要将PdfReader改为PdfFileReader
    pdf_reader = PdfReader(old_file, strict=False)
    # 没有传入密码，则直接返回读取的PDF
    if password == None:
        return pdf_reader

    # 如果使用python2需要将is_encrypted改为isEncrypted
    # 执行解密操作
    if pdf_reader.is_encrypted:
        if password is None:
            return print('文件被加密，需要密码！--{}'.format(filename))
        else:
            success = pdf_reader.decrypt(password)
            if not success :
                #return print('密码不正确！--{}'.format(filename))
                return
            else:
                #print("解密成功，密码为{}".format(password))
                return pdf_reader
    elif old_file in locals():
        old_file.close()
        # 返回结果
    return pdf_reader

def deception_pdf(root,filename, progress,progress_label,decrypted_filename=None,crack_dic=None,password=None) :
    global stop_thread
    if crack_dic is None or crack_dic == '':
        pdf_reader = get_reader(filename, password)
        if pdf_reader is None:
            return print("无内容读取")
        # 如果使用python2需要将is_encrypted改为isEncrypted
        elif not pdf_reader.is_encrypted:
            return print('文件没有被加密，无需操作')

        # 如果使用的是python2需要将PdfWriter改为PdfFileWriter
        pdf_writer = PdfWriter()

        #如果使用的是python2需要将将append_pages_from_reader改为appendPagesFromReader
        pdf_writer.append_pages_from_reader(pdf_reader)
        #创建解密后的pdf文件和展示文件的路径
        if decrypted_filename is None:
            decrypted_filename = "".join(filename.split('.')[:-1]) + '_' + '已解密' + '.pdf'
            print("解密文件已生成:{}".format(decrypted_filename))
        # 写入新文件
        pdf_writer.write(open(decrypted_filename, 'wb'))
        root.destroy()
    else :
        countlines = count_lines_in_file(crack_dic)
        progress['maximum'] = countlines
        with open(crack_dic,'r') as crack_dic_file:
            count = 1
            pdf_reader = None
            for password in crack_dic_file:
                if stop_thread:
                    break
                #print("当前的密码为{}".format(password))
                password = password.strip()
                count = count + 1
                progress['value'] = count
                progress_label['text'] = "已完成：{:.1f}%".format((progress["value"]/countlines)*100)
                root.update_idletasks()
                pdf_reader = get_reader(filename, password)
                if(pdf_reader):
                    # 如果使用的是python2需要将PdfWriter改为PdfFileWriter
                    pdf_writer = PdfWriter()
                    #如果使用的是python2需要将将append_pages_from_reader改为appendPagesFromReader
                    pdf_writer.append_pages_from_reader(pdf_reader)
                    #创建解密后的pdf文件和展示文件的路径
                    if decrypted_filename is None:
                        decrypted_filename = "".join(filename.split('.')[:-1]) + '_' + '已解密' + '.pdf'
                        #print("解密文件已生成:{},原来的密码为:{}".format(decrypted_filename,password))
                    # 写入新文件
                    pdf_writer.write(open(decrypted_filename, 'wb'))
                    messagebox.showinfo("解密完成", "您的PDF文件已破解完成，密码为{}".format(password))
                    root.destroy()
                    break
                else:
                    if(count<countlines):
                        #print(("正在尝试第{}个密码==>{}".format(count,password)))
                        pass
                    else:
                        if(pdf_reader==None):
                            messagebox.showinfo("标题", "抱歉,未找到匹配的密码，请换个字典库再试一下吧")
                            break

def closeApp(root):
    root.destroy()

def start_deception_pdf_thread(root, filename, progress, progress_label, crack_dic, decrypted_filename=None,password=None):
    global stop_thread
    stop_thread = False  # 重置终止标志
    thread = threading.Thread(target=deception_pdf, args=(root, filename , progress, progress_label, decrypted_filename,crack_dic,password))
    thread.start()

# 创建主函数
def main():
    # 创建主窗口
    root = tk.Tk()
    root.title("PDF解密小工具")
    root.geometry("600x300")
    root.resizable(False, False)
    # 创建一个Tkinter变量来存储文件路径
    #input_file_path_var = tk.StringVar()
    # 输入文件框
    input_frame = tk.Frame(root)
    input_frame.grid(column=0, row=0, padx=0, pady=10,sticky="w")
    input_label = tk.Label(input_frame, text="输入文件:")
    input_label.grid(column=0, row=0, padx=17, pady=10,sticky="w")
    input_file_path_var = tk.Entry(input_frame, width=50)
    input_file_path_var.grid(column=1, row=0, padx=10, pady=10,sticky="e")
    input_button = tk.Button(input_frame, text="选择文件", command=lambda:select_input_file(root,input_file_path_var))
    input_button.grid(column=2, row=0, padx=6, pady=10,sticky="e")
    # 选择密码字典
    dic_frame = tk.Frame(root)
    dic_frame.grid(column=0, row=1, padx=0, pady=10,sticky="w")
    dic_frame_label = tk.Label(dic_frame, text="请选择密码字典:")
    dic_frame_label.grid(column=1, row=1, padx=0, pady=10,sticky="w")
    input_crack_dic_var = tk.Entry(dic_frame, width=50)
    input_crack_dic_var.grid(column=2, row=1, padx=10, pady=10,sticky="e")
    dic_frame_button = tk.Button(dic_frame, text="选择文件", command=lambda:select_cracker_dic(root,input_crack_dic_var))
    dic_frame_button.grid(column=3, row=1, padx=5, pady=10,sticky="e")
    # 创建进度条
    progress_frame= tk.Frame(root)
    progress_frame.grid(column=0, row=4, padx=5, pady=10,sticky="w")
    progress = ttk.Progressbar(progress_frame, style='custom.Horizontal.TProgressbar', orient='horizontal', length=450, mode='determinate')

    # 创建并配置进度条样式
    style = ttk.Style()
    # 定义进度条的样式（这里以设置背景色为例）
    style.configure('custom.Horizontal.TProgressbar', background='blue')
    progress.grid(column=0, row=4, padx=0, pady=10)
    progress_label = tk.Label(progress_frame, text="已完成0%")
    progress_label.grid(column=1, row=4,padx=10, pady=10,sticky="w")
    controller_frame=tk.Frame(root)
    controller_frame.grid(column=0, row=3, padx=0, pady=10)

    # 创建按钮用于执行脚本
    execute_button = tk.Button(controller_frame, text="执行脚本", command=lambda:start_deception_pdf_thread(root,input_file_path_var.get(),progress,progress_label,input_crack_dic_var.get()))
    execute_button.grid(column=0, row=3, padx=10, pady=10)
    exit_button = tk.Button(controller_frame,text="取消并退出",command=lambda:closeApp(root))
    exit_button.grid(column=1, row=3, padx=10, pady=10)


    # 运行Tkinter事件循环
    root.mainloop()

if __name__ == "__main__":
    main()
